package com.example.hw.testcling;

import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.fourthline.cling.android.AndroidUpnpService;
import org.fourthline.cling.android.AndroidUpnpServiceImpl;
import org.fourthline.cling.android.FixedAndroidLogHandler;
import org.fourthline.cling.controlpoint.ActionCallback;
import org.fourthline.cling.controlpoint.SubscriptionCallback;
import org.fourthline.cling.model.UnsupportedDataException;
import org.fourthline.cling.model.action.ActionArgumentValue;
import org.fourthline.cling.model.action.ActionInvocation;
import org.fourthline.cling.model.gena.CancelReason;
import org.fourthline.cling.model.gena.GENASubscription;
import org.fourthline.cling.model.gena.RemoteGENASubscription;
import org.fourthline.cling.model.message.UpnpResponse;
import org.fourthline.cling.model.message.header.UDADeviceTypeHeader;
import org.fourthline.cling.model.message.header.UDAServiceTypeHeader;
import org.fourthline.cling.model.meta.Action;
import org.fourthline.cling.model.meta.Device;
import org.fourthline.cling.model.meta.LocalDevice;
import org.fourthline.cling.model.meta.RemoteDevice;
import org.fourthline.cling.model.meta.Service;
import org.fourthline.cling.model.state.StateVariableValue;
import org.fourthline.cling.model.types.BooleanDatatype;
import org.fourthline.cling.model.types.Datatype;
import org.fourthline.cling.model.types.InvalidValueException;
import org.fourthline.cling.model.types.ServiceId;
import org.fourthline.cling.model.types.StringDatatype;
import org.fourthline.cling.model.types.UDADeviceType;
import org.fourthline.cling.model.types.UDAServiceId;
import org.fourthline.cling.model.types.UDAServiceType;
import org.fourthline.cling.registry.DefaultRegistryListener;
import org.fourthline.cling.registry.Registry;

import java.util.Map;

/**
 * @author hdz
 * @date 2018/07/17
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemClickListener {

    public static final String TAG = "TestCling";

    private ArrayAdapter<DeviceDisplay> mListAdapter;

    private BrowseRegistryListener mRegistryListener = new BrowseRegistryListener();

    private AndroidUpnpService mUpnpService;
    private Service mService;
    private TextView mTxtInfo;

    private ServiceConnection serviceConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            mUpnpService = (AndroidUpnpService) service;

            // Clear the list
            mListAdapter.clear();

            // Get ready for future device advertisements
            mUpnpService.getRegistry().addListener(mRegistryListener);

            // Now add all devices to the list we already know about
            for (Device device : mUpnpService.getRegistry().getDevices()) {
                mRegistryListener.deviceAdded(device);
            }

            // Search asynchronously for all devices, they will respond soon
            mUpnpService.getControlPoint().search();
        }

        public void onServiceDisconnected(ComponentName className) {
            mUpnpService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnSearchAll = (Button)findViewById(R.id.btnSearchAll);
        btnSearchAll.setOnClickListener(this);
        Button btnSearchBinaryLight = (Button)findViewById(R.id.btnSearchBinaryLight);
        btnSearchBinaryLight.setOnClickListener(this);
        Button btnSearchSwitchPower = (Button)findViewById(R.id.btnSearchSwitchPower);
        btnSearchSwitchPower.setOnClickListener(this);

        Button btnLightOn = (Button)findViewById(R.id.btnLightOn);
        btnLightOn.setOnClickListener(this);
        Button btnLightOff = (Button)findViewById(R.id.btnLightOff);
        btnLightOff.setOnClickListener(this);
        Button btnGetStatus = (Button)findViewById(R.id.btnGetStatus);
        btnGetStatus.setOnClickListener(this);

        Button btnSetValue = (Button)findViewById(R.id.btnSetValue);
        btnSetValue.setOnClickListener(this);
        Button btnGetValue = (Button)findViewById(R.id.btnGetValue);
        btnGetValue.setOnClickListener(this);
        Button btnGetValue3 = (Button)findViewById(R.id.btnGetValue3);
        btnGetValue3.setOnClickListener(this);

        Button btnSetValues = (Button)findViewById(R.id.btnSetValues);
        btnSetValues.setOnClickListener(this);
        Button btnGetValueByInput = (Button)findViewById(R.id.btnGetValueByInput);
        btnGetValueByInput.setOnClickListener(this);

        mTxtInfo = (TextView)findViewById(R.id.txtInfo);

        ListView listView = (ListView) findViewById(R.id.allDevList);
        mListAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
        listView.setAdapter(mListAdapter);
        listView.setOnItemClickListener(this);

        // Fix the logging integration between java.util.logging and Android internal logging
        org.seamless.util.logging.LoggingUtil.resetRootHandler(new FixedAndroidLogHandler());
        // Now you can enable logging as needed for various categories of Cling:
        // Logger.getLogger("org.fourthline.cling").setLevel(Level.FINEST);

        // This will start the UPnP service if it wasn't already started
        getApplicationContext().bindService(
                new Intent(this, AndroidUpnpServiceImpl.class),
                serviceConnection,
                Context.BIND_AUTO_CREATE
        );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mUpnpService != null) {
            mUpnpService.getRegistry().removeListener(mRegistryListener);
        }
        // This will stop the UPnP service if nobody else is bound to it
        getApplicationContext().unbindService(serviceConnection);
    }

    private void searchAllDevices() {
        if (mUpnpService == null) {
            Log.d(TAG, "searchAllDevices: mUpnpService == null");
            return;
        }
        Toast.makeText(this, "搜索网络中的设备", Toast.LENGTH_SHORT).show();
        // 移除所有设备
        mUpnpService.getRegistry().removeAllRemoteDevices();
        // 搜索网络中的服务设备
        mUpnpService.getControlPoint().search();
    }
    private void searchBinaryLight() {
        if (mUpnpService == null) {
            Log.d(TAG, "searchBinaryLight: mUpnpService == null");
            return;
        }
        Toast.makeText(this, "搜索网络中的设备", Toast.LENGTH_SHORT).show();
        // 移除所有设备
        mUpnpService.getRegistry().removeAllRemoteDevices();
        // 搜索网络中的服务设备BinaryLight
        UDADeviceType udaType = new UDADeviceType("BinaryLight");
        mUpnpService.getControlPoint().search(new UDADeviceTypeHeader(udaType));
    }
    private void searchSwitchPower() {
        if (mUpnpService == null) {
            Log.d(TAG, "searchSwitchPower: mUpnpService == null");
            return;
        }
        Toast.makeText(this, "搜索网络中的设备", Toast.LENGTH_SHORT).show();
        // 移除所有设备
        mUpnpService.getRegistry().removeAllRemoteDevices();
        // 搜索网络中的服务设备SwitchPower
        UDAServiceType serviceUdaType = new UDAServiceType("SwitchPower");
        mUpnpService.getControlPoint().search(new UDAServiceTypeHeader(serviceUdaType));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnSearchAll:
                searchAllDevices();
                break;
            case R.id.btnSearchBinaryLight:
                searchBinaryLight();;
                break;
            case R.id.btnSearchSwitchPower:
                searchSwitchPower();
                break;
            case R.id.btnLightOn:
                setTarget(true);
                testGetStr(true);
                break;
            case R.id.btnLightOff:
                setTarget(false);
                testGetStr(false);
                break;
            case R.id.btnGetStatus:
                getStatus();
                break;
            case R.id.btnSetValue:
                setInfo("AAAABBBBCCCCDD");
                break;
            case R.id.btnGetValue:
                getInfo();
                break;
            case R.id.btnGetValue3:
                getDefaultConnectionService();
                break;
            case R.id.btnSetValues:
                setValues("AA", "BB", "CC");
                break;
            case R.id.btnGetValueByInput:
                mTxtInfo.setText("");
                break;
            default:
                break;
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        // 设置对话框标题
        builder.setTitle("设备信息");

        // 将设备细节信息显示到对话框
        DeviceDisplay deviceDisplay = (DeviceDisplay)parent.getAdapter().getItem(position);
        mService = deviceDisplay.getService();
        builder.setMessage(deviceDisplay.getDetailsMessage());

        // 订阅事件
        subscriptionEvents();

        //增加确定按钮
        builder.setPositiveButton("确定", null);

        //设置按钮是否可以按返回键取消,false则不可以取消
        builder.setCancelable(false);
        //创建对话框
        final AlertDialog dialog = builder.create();
        //设置弹出框失去焦点是否隐藏,即点击屏蔽其它地方是否隐藏
        dialog.setCanceledOnTouchOutside(false);

        //显示对话框，这里必须要先调show()方法，后面的getButton才有效
        dialog.show();

        //处理确认按钮
        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //退出对话框
                dialog.dismiss();
            }
        });
    }

    private void subscriptionEvents() {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "subscriptionStatus: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }
        SubscriptionCallback callback = new SubscriptionCallback(mService, 6000) {

            @Override
            public void established(GENASubscription sub) {
                System.out.println("Established: " + sub.getSubscriptionId());
            }

            @Override
            protected void failed(GENASubscription subscription,
                                  UpnpResponse responseStatus,
                                  Exception exception,
                                  String defaultMsg) {
                Log.e(TAG, "subscriptionInfo:failed: " + defaultMsg);
            }

            @Override
            public void ended(GENASubscription sub,
                              CancelReason reason,
                              UpnpResponse response) {
                if (reason != null) {
                    Log.d(TAG, "subscriptionStatus:ended: reason = "+reason.toString());
                }
            }

            @Override
            public void eventReceived(GENASubscription sub) {
                Log.d(TAG, "subscriptionStatus:eventReceived: Event: " + sub.getCurrentSequence().getValue());

                Map<String, StateVariableValue> values = sub.getCurrentValues();

                StateVariableValue status = values.get("Status");
                if (status != null) {
                    if (status.getDatatype() instanceof BooleanDatatype) {
                        Log.d(TAG, "subscriptionStatus:eventReceived: Status is: " + status.toString());
                    } else {
                        Log.e(TAG, "subscriptionStatus:eventReceived: Type error, Status is:" + status.toString());
                    }
                }

                StateVariableValue info = values.get("Info");
                if (info != null) {
                    if (info.getDatatype() instanceof StringDatatype) {
                        Log.d(TAG, "subscriptionStatus:eventReceived: Info is: " + info.toString());
                    } else {
                        Log.e(TAG, "subscriptionStatus:eventReceived: Type error, Info is:" + info.toString());
                    }
                }
            }

            @Override
            public void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
                Log.d(TAG, "subscriptionStatus:eventsMissed: Missed events: " + numberOfMissedEvents);
            }

            @Override
            protected void invalidMessage(RemoteGENASubscription sub,
                                          UnsupportedDataException ex) {
                ex.printStackTrace();
            }
        };
        mUpnpService.getControlPoint().execute(callback);
    }

    void getInfo() {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "getInfo: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }

        Action action = mService.getAction("GetInfo");
        if (action == null) {
            Log.d(TAG, "getInfo: action == null");
            return;
        }
        ActionInvocation invocation = new ActionInvocation(action);
        mUpnpService.getControlPoint().execute(new ActionCallback(invocation) {
                   @Override
                   public void success(ActionInvocation invocation) {

                       Log.d(TAG, "getInfo: Successfully called action!");
                       //Log.d("TestCling", "getStatus: actionArgumentValue = " + invocation.getOutput()[0]);

                       ActionArgumentValue actionArgumentValue = invocation.getOutput("RetInfo");
                       Log.d("TestCling", "getInfo: actionArgumentValue = " + actionArgumentValue);
                       mTxtInfo.setText(actionArgumentValue.toString());
                   }
                   @Override
                   public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                       System.err.println(defaultMsg);
                   }
               }
        );
    }

    void getDefaultConnectionService() {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "getDefaultConnectionService: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }
        if (!mService.getDevice().getDisplayString().equals("Xiaomi MiWiFi Router")) {
            Log.d(TAG, "getDefaultConnectionService: "+mService.getDevice().getDisplayString());
            return;
        }
        Action action = mService.getAction("GetDefaultConnectionService");
        if (action == null) {
            Log.d(TAG, "getDefaultConnectionService: action == null");
            return;
        }
        ActionInvocation invocation = new ActionInvocation(action);
        mUpnpService.getControlPoint().execute(new ActionCallback(invocation) {
                @Override
                public void success(ActionInvocation invocation) {

                    Log.d(TAG, "getDefaultConnectionService: Successfully called action!");
                    //Log.d("TestCling", "getStatus: actionArgumentValue = " + invocation.getOutput()[0]);

                    ActionArgumentValue actionArgumentValue = invocation.getOutput("NewDefaultConnectionService");
                    Log.d("TestCling", "getDefaultConnectionService: actionArgumentValue = " + actionArgumentValue);
                    mTxtInfo.setText(actionArgumentValue.toString());
                }
                @Override
                public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                  System.err.println(defaultMsg);
                }
            }
        );
    }

    void testGetStr(boolean b) {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "testGetStr: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }
        Action action = mService.getAction("TestGetStr");
        if (action == null) {
            Log.d(TAG, "testGetStr: action == null");
            return;
        }
        ActionInvocation invocation = new ActionInvocation(action);
        try {
            invocation.setInput("Str", b);
        } catch (InvalidValueException e) {
            e.printStackTrace();
            return;
        }

        mUpnpService.getControlPoint().execute(new ActionCallback(invocation) {
               @Override
               public void success(ActionInvocation invocation) {

                   Log.d(TAG, "testGetStr: Successfully called action!");
                   //Log.d("TestCling", "getStatus: actionArgumentValue = " + invocation.getOutput()[0]);

                   ActionArgumentValue actionArgumentValue = invocation.getOutput("RetStr");
                   Log.d("TestCling", "testGetStr: actionArgumentValue = " + actionArgumentValue);
                   mTxtInfo.setText(actionArgumentValue.toString());
               }
               @Override
               public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                   System.err.println(defaultMsg);
               }
           }
        );
    }

    void setTarget(boolean bool) {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "setTarget: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }

        Action action = mService.getAction("SetTarget");
        if (action == null) {
            Log.d(TAG, "setTarget: action == null");
            return;
        }
        ActionInvocation invocation = new ActionInvocation(action);
        try {
            invocation.setInput("NewTargetValue", bool);
        } catch (InvalidValueException e) {
            e.printStackTrace();
            return;
        }

        // Executes asynchronous in the background
        mUpnpService.getControlPoint().execute(new ActionCallback(invocation) {
                    @Override
                    public void success(ActionInvocation invocation) {
                        //assert invocation.getOutput().length == 0;
                        Log.d(TAG, "setTarget: Successfully called action!");
                    }
                    @Override
                    public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                        System.err.println(defaultMsg);
                    }
                }
        );
    }
    void setInfo(String info) {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "setInfo: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }

        Action action = mService.getAction("SetInfo");
        if (action == null) {
            Log.d(TAG, "setInfo: action == null");
            return;
        }
        ActionInvocation invocation = new ActionInvocation(action);
        try {
            invocation.setInput("NewInfo", info);
        } catch (InvalidValueException e) {
            e.printStackTrace();
            return;
        }

        // Executes asynchronous in the background
        mUpnpService.getControlPoint().execute(
            new ActionCallback(invocation) {

                @Override
                public void success(ActionInvocation invocation) {
                    //assert invocation.getOutput().length == 0;
                    Log.d(TAG, "setInfo: Successfully called action!");
                }

                @Override
                public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                    System.err.println(defaultMsg);
                }
            }
        );
    }
    void getStatus() {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "getStatus: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }
        Action action = mService.getAction("GetStatus");
        if (action == null) {
            Log.d("TestCling", "getStatus: action == null");
            return;
        }

        ActionInvocation invocation = new ActionInvocation(action);

        // Executes asynchronous in the background
        mUpnpService.getControlPoint().execute(
            new ActionCallback(invocation) {

                @Override
                public void success(ActionInvocation invocation) {

                    Log.d(TAG, "getStatus: Successfully called action!");
                    //Log.d("TestCling", "getStatus: actionArgumentValue = " + invocation.getOutput()[0]);

                    ActionArgumentValue actionArgumentValue = invocation.getOutput("ResultStatus");
                    Log.d("TestCling", "getStatus: actionArgumentValue = " + actionArgumentValue);
                    mTxtInfo.setText(actionArgumentValue.toString());
                }

                @Override
                public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                    System.err.println(defaultMsg);
                }
            }
        );
    }
    private void setValues(String value1, String value2, String value3) {
        if (mUpnpService ==null || mService==null) {
            Log.d(TAG, "setValues: mUpnpService=" + mUpnpService + ",mService=" + mService);
            return;
        }

        Action action = mService.getAction("SetValues");
        if (action == null) {
            Log.d(TAG, "setValues: action == null");
            return;
        }
        ActionInvocation invocation = new ActionInvocation(action);
        try {
            invocation.setInput("Value1", value1);
            invocation.setInput("Value2", value2);
            invocation.setInput("Value3", value3);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        // Executes asynchronous in the background
        mUpnpService.getControlPoint().execute(
            new ActionCallback(invocation) {

                @Override
                public void success(ActionInvocation invocation) {
                    //assert invocation.getOutput().length == 0;
                    Log.d(TAG, "setValues: Successfully called action!");
                }

                @Override
                public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                    System.err.println(defaultMsg);
                }
            }
        );
    }


    private class BrowseRegistryListener extends DefaultRegistryListener {

        ServiceId serviceId = new UDAServiceId("SwitchPower");

        /* Discovery performance optimization for very slow Android devices! */
        @Override
        public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice device) {
            deviceAdded(device);
        }

        @Override
        public void remoteDeviceDiscoveryFailed(Registry registry, final RemoteDevice device, final Exception ex) {
            runOnUiThread(new Runnable() {
                public void run() {
                    Toast.makeText(
                            MainActivity.this,
                            "Discovery failed of '" + device.getDisplayString() + "': "
                                    + (ex != null ? ex.toString() : "Couldn't retrieve device/service descriptors"),
                            Toast.LENGTH_LONG
                    ).show();
                }
            });
            deviceRemoved(device);
        }
        /* End of optimization, you can remove the whole block if your Android handset is fast (>= 600 Mhz) */

        @Override
        public void remoteDeviceAdded(Registry registry, RemoteDevice device) {
            deviceAdded(device);
/*            if ((mService = device.findService(serviceId)) != null) {
                System.out.println("======Service discovered: " + mService);
                System.out.println("======Service Details: " + device.getDisplayString());
            }*/
        }

        @Override
        public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
            deviceRemoved(device);
            Log.d("TestCling", "remoteDeviceRemoved: " + device.getDisplayString());
        }

        @Override
        public void localDeviceAdded(Registry registry, LocalDevice device) {
            deviceAdded(device);
        }

        @Override
        public void localDeviceRemoved(Registry registry, LocalDevice device) {
            deviceRemoved(device);
        }

        public void deviceAdded(final Device device) {
            runOnUiThread(new Runnable() {
                public void run() {
                    DeviceDisplay d = new DeviceDisplay(device);
                    int position = mListAdapter.getPosition(d);
                    if (position >= 0) {
                        // Device already in the list, re-set new value at same position
                        mListAdapter.remove(d);
                        mListAdapter.insert(d, position);
                    } else {
                        mListAdapter.add(d);
                    }
                }
            });
        }

        public void deviceRemoved(final Device device) {
            runOnUiThread(new Runnable() {
                public void run() {
                    mListAdapter.remove(new DeviceDisplay(device));
                }
            });
        }
    }
}
